package com.arpa.oms.controller;

import cn.hutool.core.collection.IterUtil;
import com.arpa.core.web.MediaTypes;
import com.arpa.core.web.Result;
import com.arpa.ntocc.common.common.Page;
import com.arpa.ntocc.common.common.constant.StrConsts;
import com.arpa.ntocc.common.common.exception.ServiceException;
import com.arpa.ntocc.common.common.util.UserUtil;
import com.arpa.ntocc.common.security.annotation.NeedLogin;
import com.arpa.oms.domain.dto.OmsReceivableDetailDTO;
import com.arpa.oms.domain.vo.OmsReceivableDetailVO;
import com.arpa.oms.service.IOmsReceivableDetailService;
import com.arpa.oms.service.IOmsSettlementService;
import com.arpa.wms.domain.dto.ExpenseDetailSettlementDTO;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import io.swagger.annotations.*;
import lombok.extern.log4j.Log4j2;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;


/**
 * <p>
 * OMS应收明细 Controller
 * </p>
 *
 * @author LEO
 * @since 2020-11-17
 */
@Api(tags = "OMS收款明细")
@RestController
@RequestMapping(value = "/omsReceivableDetail", produces = "application/json;charset=UTF-8")
@Log4j2(topic = "business")
public class OmsReceivableDetailController {


    @Resource
    private RedissonClient redissonClient;
    private final IOmsReceivableDetailService iOmsReceivableDetailService;

    private final IOmsSettlementService omsSettlementService;

    public OmsReceivableDetailController(
            IOmsReceivableDetailService iOmsReceivableDetailService,
            IOmsSettlementService omsSettlementService) {
        this.iOmsReceivableDetailService = iOmsReceivableDetailService;
        this.omsSettlementService = omsSettlementService;
    }

    /**
     * 获取 OMS应收明细 列表
     *
     * @param omsReceivableDetailDTO
     * @param request
     * @return
     */
    @ApiOperation(value = "OMS收款明细列表", notes = "OMS结算管理--收款明细列表")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "Authorization", value = "授权token", required = true, paramType = "header", dataType = "String", defaultValue = "Bearer "),
            @ApiImplicitParam(name = "deviceId", value = "设备id", paramType = "header", dataType = "string", defaultValue = "0000000000")
    })
    @ApiResponses(@ApiResponse(code = 0, message = "success", response = OmsReceivableDetailVO.class))
    @NeedLogin
    @GetMapping()
    public Result list(OmsReceivableDetailDTO omsReceivableDetailDTO, HttpServletRequest request) {
        try {
            omsReceivableDetailDTO.setShipmentCode(UserUtil.getLoginWarehouseCodeOrShipmentCode());
            OmsReceivableDetailVO omsReceivableDetailVOSum = iOmsReceivableDetailService.queryListSum(omsReceivableDetailDTO);
            List<OmsReceivableDetailVO> omsReceivableDetailVOList = iOmsReceivableDetailService.queryList(omsReceivableDetailDTO);

            Page returnPage = new Page(omsReceivableDetailDTO.getPageNum(), omsReceivableDetailDTO.getPageSize());
            returnPage.setRecords(omsReceivableDetailVOList);
            returnPage.setTotal(omsReceivableDetailVOSum.getTotal());
            returnPage.setSumObject(omsReceivableDetailVOSum);
            return Result.ok().data(returnPage);
        } catch (Exception e) {
            log.error(StrConsts.CATCH_MESSAGE, e);
            return Result.error(e.getMessage());
        }
    }


    /**
     * OMS收款结算
     *
     * @param settlement
     * @param request
     * @return
     */
    @ApiOperation(value = "OMS收款结算 ", notes = "OMS收款结算", produces = MediaTypes.JSON_UTF_8)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "Authorization", value = "授权token", required = true, paramType = "header", dataType = "String", defaultValue = "Bearer "),
    })
    @NeedLogin
    @PostMapping(value = "/settlement")
    public Result settlement(@RequestBody @Validated ExpenseDetailSettlementDTO settlement, HttpServletRequest request) {

        List<RLock> locks = Lists.newArrayList();
        List<String> failCodes = Lists.newArrayList();
        String failMsg = "";
        try {
            //可操作的CODE
            List<ExpenseDetailSettlementDTO.SettlementItem> execCodes = Lists.newArrayList();
            List<ExpenseDetailSettlementDTO.SettlementItem> items = settlement.getSettlementItems();
            if (IterUtil.isEmpty(items)) {
                return Result.error("请选择结算费用");
            }
            for (ExpenseDetailSettlementDTO.SettlementItem item : items) {
                RLock l = redissonClient.getLock(getSettlementLockKey(item.getCode()));
                if (l.tryLock()) {
                    log.info("结算费用【{}】获得锁", item.getCode());
                    execCodes.add(item);
                    locks.add(l);
                } else {
                    failCodes.add(item.getCode());
                    failMsg = "结算费用【" + item.getCode() + "】正在操作中";
                    log.error("结算费用【{}】正在操作中", item.getCode());
                }
            }


            // 失败的订单集合
            // 线程结果集
            Map<String, Future<Boolean>> futureMap = Maps.newHashMap();
            for (ExpenseDetailSettlementDTO.SettlementItem item : execCodes) {
                Future future = iOmsReceivableDetailService.check(item);
                if (future == null) {
                    failCodes.add(item.getCode());
                } else {
                    futureMap.put(item.getCode(), future);
                }
            }

            Iterator<Map.Entry<String, Future<Boolean>>> iterator = futureMap.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, Future<Boolean>> entry = iterator.next();
                try {
                    Boolean status = entry.getValue().get();
                    if (!status) {
                        failCodes.add(entry.getKey());
                    }
                } catch (ServiceException e) {
                    log.error("结算费用【{}】线程获取结果【{}】", entry.getKey(), e.getMessage());
                    failMsg += "<br/><font color='red'>" + e.getMessage() + ";</font>";
                    failCodes.add(entry.getKey());
                } catch (Exception e) {
                    log.error("结算费用【{}】线程获取结果【{}】", entry.getKey(), e.getMessage());
                    failMsg += "<br/><font color='red'>" + e.getCause().getMessage() + ";</font>";
                    failCodes.add(entry.getKey());
                }
            }

            if (failCodes.size() > 0) {
                return Result.error("费用结算失败:" + failMsg);
            } else {
                omsSettlementService.receivableSettlement(settlement);
                return Result.ok("费用结算成功");
            }


        } finally {
            try {
                locks.forEach(l -> {
                    // 判断锁不为空 && 当前线程获得到了锁
                    if (null != l && l.isLocked()) {
                        l.unlock();
                    }
                });
            } catch (Exception e) {
                log.error("释放锁失败！！", e);
            }

        }
    }

    /**
     * 费用结算分布式锁
     *
     * @param code
     * @return
     */
    private String getSettlementLockKey(String code) {
        return String.format("oms-settlement-code-%s", code);
    }

}
